package com.qianfeng.smartdevice.service.impl;

//
//                            _ooOoo_  
//                           o8888888o  
//                           88" . "88  
//                           (| -_- |)  
//                            O\ = /O  
//                        ____/`---'\____  
//                      .   ' \\| |// `.  
//                       / \\||| : |||// \  
//                     / _||||| -:- |||||- \  
//                       | | \\\ - /// | |  
//                     | \_| ''\---/'' | |  
//                      \ .-\__ `-` ___/-. /  
//                   ___`. .' /--.--\ `. . __  
//                ."" '< `.___\_<|>_/___.' >'"".  
//               | | : `- \`.;`\ _ /`;.`/ - ` : | |  
//                 \ \ `-. \_ __\ /__ _/ .-` / /  
//         ======`-.____`-.___\_____/___.-`____.-'======  
//                            `=---='  
//  
//         .............................................  
//                  佛祖镇楼                  BUG辟易  
//          佛曰:  
//                  写字楼里写字间，写字间里程序员；  
//                  程序人员写程序，又拿程序换酒钱。  
//                  酒醒只在网上坐，酒醉还来网下眠；  
//                  酒醉酒醒日复日，网上网下年复年。  
//                  但愿老死电脑间，不愿鞠躬老板前；  
//                  奔驰宝马贵者趣，公交自行程序员。  
//                  别人笑我忒疯癫，我笑自己命太贱；  
//  


import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.qianfeng.smartdevice.cache.CategoryCache;
import com.qianfeng.smartdevice.cache.DeviceCache;
import com.qianfeng.smartdevice.cache.SceneCache;
import com.qianfeng.smartdevice.events.DevicePowerCommandEvent;
import com.qianfeng.smartdevice.exceptions.MyBaseException;
import com.qianfeng.smartdevice.exceptions.ResultCode;
import com.qianfeng.smartdevice.mapper.DeviceMapper;
import com.qianfeng.smartdevice.pojo.*;
import com.qianfeng.smartdevice.service.DeviceService;
import com.qianfeng.smartdevice.utils.SecruitUtils;
import org.checkerframework.checker.units.qual.A;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;

import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutionException;

/**
 * Created by Jackiechan on 2022/1/7/20:03
 *
 * @author Jackiechan
 * @version 1.0
 * @since 1.0
 */

@Service
@Transactional
public class DeviceServiceImpl implements DeviceService {

    private CategoryCache categoryCache;

    private SceneCache sceneCache;

    private DeviceMapper deviceMapper;

    private ApplicationContext context;

    private DeviceCache deviceCache;

    private ObjectMapper objectMapper;

    @Autowired
    public void setObjectMapper(ObjectMapper objectMapper) {
        this.objectMapper = objectMapper;
    }

    @Autowired
    public void setDeviceCache(DeviceCache deviceCache) {
        this.deviceCache = deviceCache;
    }

    @Autowired
    public void setContext(ApplicationContext context) {
        this.context = context;
    }

    @Autowired
    public void setDeviceMapper(DeviceMapper deviceMapper) {
        this.deviceMapper = deviceMapper;
    }

    @Autowired
    public void setSceneCache(SceneCache sceneCache) {
        this.sceneCache = sceneCache;
    }

    @Autowired
    public void setCategoryCache(CategoryCache categoryCache) {
        this.categoryCache = categoryCache;
    }

    @Override
    public void addDevice(Device device) throws ExecutionException {
        // 数据进行校验
        Assert.isTrue(!device.isEmpty(CheckType.ADD),()->{
            throw new MyBaseException("传递的数据不完整", ResultCode.DATA_NOT_ALLOWED);
        });
        //分类要进行判断
        Category category = categoryCache.get(device.getCategyId());
        Assert.notNull(category,()->{
            throw new MyBaseException("分类不存在", ResultCode.DATA_NOT_EXIST);
        });
        //场景要进行判断
        MyBaseUser user = SecruitUtils.getUser();
        List<Scene> sceneList = sceneCache.getByUserId(user.getUserId());
        //获取到传递的场景对应的数量,也就是查询有没有场景
        if (device.getSceneId()==null||device.getSceneId()==-1L) {
            //默认场景
            device.setSceneId(-1L);
        }else{
            long count = sceneList.stream().filter(scene -> device.getSceneId() == scene.getSceneId()&&scene.getStatus()==1).count();
            Assert.isTrue(count>0, ()->{
                //我们传递了一个场景的id,但是没有查询到这个场景,那肯定就不行
                throw new MyBaseException("场景不存在", ResultCode.DATA_NOT_EXIST);
            });
        }
        //设备本身是否已经被绑定需要判断
        Device currentDevice = findById(device.getDeviceId());
        //如果设备被绑定到了其他人身上,那么就不行

        Assert.isTrue(currentDevice == null || currentDevice.getBindUserId() == user.getUserId(),()->{
            throw new MyBaseException("设备已经被绑定", ResultCode.DEVICE_ALREADY_BINDED);
        });

        if (currentDevice == null) {
            //绑定
            device.setBindUserId(user.getUserId());
            device.setBindTime(new Date());
            deviceMapper.addDevice(device);
        }
        //否则就是已经绑定到用户身上,可以什么都不做

    }

    @Override
    public Device findById(String deviceId) {
        Assert.hasText(deviceId,()->{
                    throw new MyBaseException("主键不存在", ResultCode.ID_NULL);
                });
        return deviceMapper.findById(deviceId);
    }


    @Override
    public void sendController(String id, String command) {
        //给设备发送命令,需要主动推送数据给设备,所以需要通过websocket找到会话进行推送
        //会话可以通过id找到,从保存有所有设备会话的地址找
        //在我们的handler中存在集合保存会话,我们从中获取数据即可
        //按照面向对象思想,向设备发送指令 websocket自己最清楚, 所以我们在这里不是直接调用websocket,而是通过event事件进行操作
        //不同的设备发送的命令的内容可能不一样
        //根据设备id找到分类, 然后从中获取到命令的内容,然后根据传递过来的操作的命令标识,找到真正要发送的命令.然后发送过去
        Device device = deviceCache.selectById(id);
        Assert.isTrue(device!=null&&device.getIsOnline()==1,()->{//设备不能为空并且必须在线
            throw new MyBaseException("设备不存在或者不在线", ResultCode.ID_NULL);
        });
        Long categyId = device.getCategyId();
        Category category = categoryCache.get(categyId);//获取到分类数据
        String txCommand = category.getTxCommand();//获取到送的数据格式
        Assert.hasText(txCommand,()->{
            throw new MyBaseException("设备不支持此操作", ResultCode.DEVICE_OPER_NOT_ALLOWED);
        });
        try {
            Map commnadMap = objectMapper.readValue(txCommand, Map.class);
            String realCommand = (String) commnadMap.get(command);//这是真正要执行的命令格式,根据用户传递的命令名称查找的
            Assert.hasText(realCommand,()->{
                throw new MyBaseException("设备不支持此操作", ResultCode.DEVICE_OPER_NOT_ALLOWED);
            });
            context.publishEvent(new DevicePowerCommandEvent(id,realCommand));
        } catch (JsonProcessingException e) {
            e.printStackTrace();
            throw new MyBaseException("设备不支持此操作", ResultCode.DEVICE_OPER_NOT_ALLOWED);
        }

    }

    @Override
    public void updateStatus(String id, Long status) {
        deviceMapper.updateStatus(id, status);
    }

    @Override
    public void updateDeviceOnlineChange(Device device) {
        deviceMapper.updateDeviceOnlineChange(device);
    }


}
